package pathfinding

import (
	"fmt"
	"sync"

	"gddgame.cc/galaxy/utils"
)

type ZCGrid interface {
	InBounds(x int16, y int16) bool
	N(point *Vector2, mask PathMask) bool
	Node(point *Vector2) (bool, PathSeatNode)
	NodeCost(point *Vector2) int
}
type ZCInterface interface {
	CubeToVector(cube *Cube) Vector2
	VectorToCube(vector *Vector2) Cube
}

var (
	zcResultQueue = sync.Pool{New: func() interface{} {
		return []DijkstraNode{}
	}}
	zcPointPool = sync.Pool{New: func() interface{} {
		return &ZeroCubePoint{}
	}}
	zcEdgePool = sync.Pool{New: func() interface{} {
		return &ZeroCubeEdge{}
	}}
	zcPointsPool = sync.Pool{New: func() interface{} {
		return []*ZeroCubePoint{}
	}}
	zcContainerPool = sync.Pool{New: func() interface{} {
		return &ZeroCubeContainer{
			PointList:   []*ZeroCubePoint{},
			EdgeList:    []*ZeroCubeEdge{},
			PointsList:  [][]*ZeroCubePoint{},
			Queue:       []uint16{},
			VisitedList: []int{},
			CostList:    []int{},
			Blocks:      utils.UInt16Slice{},
		}
	}}
)

type ZeroCubeContainer struct {
	Range      *ZeroCube
	EdgeList   []*ZeroCubeEdge
	PointList  []*ZeroCubePoint
	PointsList [][]*ZeroCubePoint

	StartPoint *ZeroCubePoint
	EndPoint   *ZeroCubePoint

	Queue []uint16

	VisitedList []int
	CostList    []int

	Blocks utils.UInt16Slice
}

func (container *ZeroCubeContainer) EndArea(area *ZeroCubeArea) bool {
	return area == container.EndPoint.Area
}

func (container *ZeroCubeContainer) Point() *ZeroCubePoint {
	point := zcPointPool.Get().(*ZeroCubePoint)
	//point.Index = 0
	point.Area = nil
	point.FromArea = nil
	point.Step = 0
	point.NodeCost = 0
	point.Container = container
	container.PointList = append(container.PointList, point)
	return point
}

func (container *ZeroCubeContainer) Edge() *ZeroCubeEdge {
	edge := zcEdgePool.Get().(*ZeroCubeEdge)
	//edge := &ZeroEdge{}
	container.EdgeList = append(container.EdgeList, edge)
	return edge
}

func (container *ZeroCubeContainer) Points() []*ZeroCubePoint {
	points := zcPointsPool.Get().([]*ZeroCubePoint)
	//points := []*ZeroPoint{}
	points = points[0:0]
	container.PointsList = append(container.PointsList, points)
	return points
}

func (container *ZeroCubeContainer) Visited(l int) ([]int, []int) {
	for index, v := range container.VisitedList {
		if v == 0 {
			continue
		}
		container.VisitedList[index] = 0
	}
	if len(container.VisitedList) > l {
		return container.VisitedList[0:l], container.CostList[0:l]
	}
	container.VisitedList = append(container.VisitedList, make([]int, l-len(container.VisitedList))...)
	container.CostList = append(container.CostList, make([]int, l-len(container.CostList))...)
	return container.VisitedList[0:l], container.CostList[0:l]
}
func (container *ZeroCubeContainer) List() []uint16 {
	//container.Queue.Init()
	return container.Queue
}

func (container *ZeroCubeContainer) Block() utils.UInt16Slice {
	//container.Queue.Init()
	return container.Blocks[0:0]
}

func (container *ZeroCubeContainer) Duplicate(strs []uint16) []uint16 {
	result := []uint16{}
	tempMap := map[uint16]struct{}{} // 存放不重复主键
	for _, e := range strs {
		l := len(tempMap)
		tempMap[e] = struct{}{}
		if len(tempMap) != l { // 加入map后，map长度变化，则元素不重复
			result = append(result, e)
		}
	}
	return result
}

func (container *ZeroCubeContainer) Release() {
	for _, edge := range container.EdgeList {
		zcEdgePool.Put(edge)
	}
	container.EdgeList = container.EdgeList[0:0]
	for _, point := range container.PointList {
		zcPointPool.Put(point)
	}
	container.PointList = container.PointList[0:0]
	for _, points := range container.PointsList {
		zcPointsPool.Put(points)
	}
	container.PointsList = container.PointsList[0:0]

	//container.VisitedList = container.VisitedList[0:0]
}

type ZeroCube struct {
	Gap int16

	Interface ZRInterface
	Grid      ZRGrid
	Dijkstra  *Dijkstra
	Zero      *Cube

	AreaMap      map[Cube]*ZeroCubeArea
	AreaNeighbor []Cube
	AreaOffset   []Cube
	Offset       Vector2
}

func NewZeroCube(gap int16, i ZRInterface) *ZeroCube {
	zc := &ZeroCube{
		Gap:       gap,
		Interface: i,
		Dijkstra:  NewDijkstra(),

		Zero:    &Cube{0, 0, 0},
		AreaMap: map[Cube]*ZeroCubeArea{},
		AreaNeighbor: []Cube{
			{int16(2 * gap), int16(-1 * gap), int16(-1 * gap)}, Cube{int16(1 * gap), int16(1 * gap), int16(-2 * gap)}, Cube{int16(-1 * gap), int16(2 * gap), int16(-1 * gap)},
			Cube{int16(-2 * gap), int16(1 * gap), int16(1 * gap)}, Cube{int16(-1 * gap), int16(-1 * gap), int16(2 * gap)}, Cube{int16(1 * gap), int16(-2 * gap), int16(1 * gap)},

			//{2 * gap, -2 * gap, 0}, {2 * gap, 0, -2 * gap}, {0, -2 * gap, 2 * gap},
			//{0, 2 * gap, -2 * gap}, {-2 * gap, 0, 2 * gap}, {-2 * gap, 2 * gap, 0},
		},
		AreaOffset: []Cube{
			Cube{int16(1 * gap), int16(1 * gap), int16(-2 * gap)}, Cube{int16(-1 * gap), int16(2 * gap), int16(-1 * gap)},
			Cube{int16(1 * gap), int16(-2 * gap), int16(1 * gap)}, Cube{int16(-1 * gap), int16(-1 * gap), int16(2 * gap)},
		},
		Offset: Vector2{
			X: 2 * gap,
			Y: 3 * gap,
		},
	}
	//for _, r := range zc.AreaNeighbor {
	//	fmt.Println(r, r.ToEvenQ(), zc.Offset)
	//}
	return zc
}

func (zc *ZeroCube) BindGrid(grid ZRGrid) {
	zc.Grid = grid
}

func (zc *ZeroCube) registerArea(areaCube *Cube) *ZeroCubeArea {
	v := zc.Interface.CubeToVector(areaCube)
	area := &ZeroCubeArea{
		Range:    zc,
		Grid:     zc.Grid,
		Center:   areaCube,
		Point:    &v,
		Neighbor: nil,
		//ChildrenCube:   nil,
		//ChildrenOffset: nil,
	}
	if area.Init() {
		//fmt.Println("area register", areaCube, zc.Interface.CubeToVector(areaCube))
		zc.AreaMap[*areaCube] = area
		return area
	} else {
		return nil
	}
}

func (zc *ZeroCube) InitArea() {
	// 生成原点
	area := zc.registerArea(zc.Zero)
	if area == nil {
		return
	}
	area.Link()

	// 预处理所有area
	for _, area := range zc.AreaMap {
		area.Update()
	}
}
func (zc *ZeroCube) Plan(start Vector2, goal Vector2, mask PathMask, complete bool, result []Vector2) (bool, []Vector2) {
	if !zc.Grid.N(&goal, mask) {
		//ok, node := zc.grid.Node(&goal)
		//fmt.Println(1)
		return false, result
	}
	if start.Equals(&goal) {
		return true, append(result, start, goal)
	}

	startArea := zc.GetCenter(&start)
	//fmt.Println("start", startArea.Center, startIndex)
	if startArea != nil {
		endArea := zc.GetCenter(&goal)
		//fmt.Println("end", endArea.Center, endIndex)
		if endArea != nil {
			container := zcContainerPool.Get().(*ZeroCubeContainer)
			container.Range = zc

			startPoint := container.Point()
			startPoint.Cube = start.EvenQToCube()
			startPoint.Index = startArea.CubeMap[startPoint.Cube]
			startPoint.Area = startArea
			//startPoint.FromArea = startArea
			//startPoint.IsHead = true

			endPoint := container.Point()
			endPoint.Cube = goal.EvenQToCube()
			endPoint.Index = endArea.CubeMap[endPoint.Cube]
			endPoint.Area = endArea
			//endPoint.FromArea = endArea
			//endPoint.IsHead = true
			container.StartPoint = startPoint
			container.EndPoint = endPoint
			// 一块区域范围内，直接寻路
			if startPoint.Area == endPoint.Area || DistanceWithCube(&startPoint.Cube, &endPoint.Cube) <= int(zc.Gap) {
				result = append(result, start, goal)
			} else {
				rr := zcResultQueue.Get().([]DijkstraNode)
				rr = rr[0:0]
				r, _ := zc.Dijkstra.ToDijkstra(startPoint, endPoint, mask, &rr, RouteLogicInf)
				if r {
					for _, n := range rr {
						nn := n.(*ZeroCubePoint)
						result = append(result, nn.Cube.ToEvenQ())
					}
				}
				zcResultQueue.Put(rr)
			}
			container.Release()
			zcContainerPool.Put(container)
			if len(result) == 0 {
				fmt.Println("plan start", result, start, start.EvenQToCube(), startPoint.GetId(), startArea.Center, DistanceWithCube(&startPoint.Cube, startArea.Center), goal, goal.EvenQToCube(), endPoint.GetId(), endArea.Center, DistanceWithCube(&endPoint.Cube, endArea.Center))
			}
		} else {
			fmt.Println(2)
		}
	} else {
		fmt.Println(3)
	}

	return len(result) > 0, result
}

func (zc *ZeroCube) GetArea(point *Vector2) *ZeroCubeArea {
	cube := zc.Interface.VectorToCube(point)
	areaCube := zc.Area(cube)
	if area, ok := zc.AreaMap[areaCube]; ok {
		//fmt.Println(areaCube, zc.Interface.CubeToVector(&areaCube), DistanceWithCube(&areaCube, &cube), zc.Gap)
		return area
	}
	//fmt.Println(areaCube, zc.Interface.CubeToVector(&areaCube), DistanceWithCube(&areaCube, &cube), zc.Gap, zc.AreaMap[areaCube])
	return nil
}

func (zc *ZeroCube) GetCenter(point *Vector2) *ZeroCubeArea {
	cube := zc.Interface.VectorToCube(point)
	areaCube := zc.Center(cube)
	if area, ok := zc.AreaMap[areaCube]; ok {
		//fmt.Println(point, areaCube, zc.Interface.CubeToVector(&areaCube), DistanceWithCube(&areaCube, &cube), zc.Gap)
		return area
	}
	//fmt.Println(areaCube, zc.Interface.CubeToVector(&areaCube), DistanceWithCube(&areaCube, &cube), zc.Gap, zc.AreaMap[areaCube])
	return nil
}
func (zc *ZeroCube) Area(cube Cube) Cube {
	gap := int16(zc.Gap)
	q := cube.Q / gap
	r := cube.R / gap
	s := cube.S / gap

	qt := cube.Q % gap
	if qt > 0 {
		q += 1
	} else if qt < 0 {
		q -= 1
	}
	rt := cube.R % gap
	if rt > 0 {
		r += 1
	} else if rt < 0 {
		r -= 1
	}
	st := cube.S % gap
	if st > 0 {
		s += 1
	} else if st < 0 {
		s -= 1
	}
	qz := q % 3
	rz := r % 3
	sz := s % 3
	qx := q - qz
	rx := r - rz
	sx := s - sz
	//flag := false
	//fmt.Println(cube, qx, rx, qz, rz, sx, sz, qt, rt, st)
	v, ok := ZeroAllCompose[Vector2{qz, rz}]
	if ok {
		if v == nil {
			qx += qz
			rx += rz
		} else if v.Y == 0 {
			qx += ZeroCompose[v.X].X
			rx += ZeroCompose[v.X].Y
		} else if v.X == 0 {
			qx += ZeroCompose[v.Y].X
			sx += ZeroCompose[v.Y].Y

			qr := qx * gap
			ss := sx * gap
			//fmt.Println(cube, qx, rx, qz, rz, sx, sz)
			return Cube{qr, -qr - ss, ss}
		} else {
			d1 := ZeroCompose[v.X]
			d2 := ZeroCompose[v.Y]
			a := Cube{Q: (qx + d1.X) * gap, R: (rx + d1.Y) * gap}
			a.Fix()
			di1 := DistanceWithCube(&cube, &a)
			b := Cube{Q: (qx + d2.X) * gap, R: (rx + d2.Y) * gap}
			b.Fix()
			di2 := DistanceWithCube(&cube, &b)
			//fmt.Println("test", v, di1, di2)
			if di1 > di2 {
				return b
			} else {
				return a
			}
		}
	} else {
		a := Cube{Q: qx * gap, R: rx * gap}
		a.Fix()
		if int16(DistanceWithCube(&cube, &a)) > gap {
			for _, n := range zc.AreaNeighbor {
				b := a.Offset(n.Q, n.R, n.S)
				d := DistanceWithCube(&cube, &b)
				if int16(d) <= gap {
					return b
				}
			}
		}
		return a
	}

	qr := qx * gap
	rq := rx * gap
	//ss := sx * zc.Gap
	return Cube{qr, rq, -qr - rq}
}

func (zc *ZeroCube) Center(cube Cube) Cube {
	position := cube.ToEvenQ()
	center := Vector2{}
	r := position.X / zc.Offset.X
	center.X = r * zc.Offset.X
	x := position.X - center.X
	if x > 0 {
		if x*2 > zc.Offset.X {
			center.X += zc.Offset.X
		}
	} else if x < 0 {
		if x*-2 > zc.Offset.X {
			center.X -= zc.Offset.X
		}
	}
	//fmt.Println("x", position.X, zc.Offset.X, r, x, center.X)

	r = position.Y / zc.Offset.Y
	center.Y = r * zc.Offset.Y
	y := position.Y - center.Y
	if y > 0 {
		if y*2 > zc.Offset.Y {
			center.Y += zc.Offset.Y
		}
	} else if y < 0 {
		if y*-2 > zc.Offset.Y {
			center.Y -= zc.Offset.Y
		}
	}
	//fmt.Println("y", position.Y, zc.Offset.Y, r, y, center.Y)

	cb := center.EvenQToCube()
	d := DistanceWithCube(&cube, &cb)
	//fmt.Println("d", center, cb, d, zc.Gap)
	if int16(d) <= zc.Gap {
		return cb
	}
	index := 0
	if position.X-center.X < 0 {
		index += 1
	}
	if position.Y-center.Y < 0 {
		index += 2
	}
	i := zc.AreaOffset[index]
	//fmt.Println(i, cb, (&i).ToEvenQ(), cb.ToEvenQ())
	cb = cb.Offset(i.Q, i.R, i.S)
	//fmt.Println("d", index, cb, cb.ToEvenQ(), DistanceWithCube(&cube, &cb))
	return cb
}

type ZeroCubeArea struct {
	Range *ZeroCube
	Grid  ZRGrid

	Center         *Cube
	Point          *Vector2
	Neighbor       []*ZeroCubeArea
	ChildrenOffset []Vector2

	RingStartIndex uint16
	RingList       []*ZeroCubeLink

	//PointList [][]uint16
	//IndexMap  map[Vector2]uint16
	CubeMap map[Cube]uint16
}

func (za *ZeroCubeArea) Init() bool {
	// 初始化必要数据
	//fmt.Println(za.Point, za.Center)
	ranges := []Cube{*za.Center}
	flag := false
	for i := 0; i <= int(za.Range.Gap); i++ {
		if i == int(za.Range.Gap) {
			za.RingStartIndex = uint16(len(ranges))
		}
		l := HexRingDistance(za.Center, i, 4)
		for _, k := range l {
			point := za.Range.Interface.CubeToVector(&k)
			if za.Grid.InBounds(point.X, point.Y) {
				flag = true
			}
			ranges = append(ranges, k)
		}
	}
	//fmt.Println(za.Center, za.Range.Interface.CubeToVector(za.Center), ranges)
	if !flag {
		//fmt.Println(za.Center, za.Range.Interface.CubeToVector(za.Center))
		return false
	}
	//ranges := HexRangeDistance(za.Center, int(za.Range.Gap))
	//za.ChildrenCube = ranges
	za.CubeMap = map[Cube]uint16{}
	//za.IndexMap = make(map[Vector2]uint16, len(ranges))
	za.ChildrenOffset = make([]Vector2, len(ranges))
	//za.PointList = make([][]uint16, len(ranges))
	for index, cube := range ranges {
		za.CubeMap[cube] = uint16(index)
		offset := za.Range.Interface.CubeToVector(&cube)
		za.ChildrenOffset[index] = offset
		//za.IndexMap[offset] = uint16(index)
	}
	//for index, cube := range ranges {
	//	var tmp Cube
	//	za.PointList[uint16(index)] = []uint16{}
	//	for d, _ := range CubeDirection {
	//		tmp = cube
	//		tmp.Move(&CubeDirection[d])
	//		i, ok := cubeIndexMap[tmp]
	//		if !ok {
	//			continue
	//		}
	//		za.PointList[uint16(index)] = append(za.PointList[uint16(index)], i)
	//	}
	//}
	//fmt.Println(za.Center, za.Center.ToEvenQ(), ranges, len(za.ChildrenOffset))

	rings := HexRingDistance(za.Center, int(za.Range.Gap), 4)
	//za.ChildrenRing = make([]uint16, len(rings))
	za.RingList = make([]*ZeroCubeLink, len(rings))
	//var tmp *ZeroCubeLink
	//var first *ZeroCubeLink
	var link *ZeroCubeLink
	for _, cube := range rings {
		i := za.CubeMap[cube]
		link = &ZeroCubeLink{
			//Left:     tmp,
			//Right:    nil,
			Neighbor: make([]byte, 0, 1),
		}
		//if tmp != nil {
		//	tmp.Right = link
		//} else {
		//	first = link
		//}
		//tmp = link
		za.RingList[i-za.RingStartIndex] = link
	}
	//if first != nil && tmp != nil {
	//	first.Left = tmp
	//	tmp.Right = first
	//}
	za.Neighbor = make([]*ZeroCubeArea, len(CubeDirection))
	return true
}

func (za *ZeroCubeArea) Link() {
	for index, d := range za.Range.AreaNeighbor {
		if za.Neighbor[index] != nil {
			continue
		}
		areaCube := za.Center.Offset(d.Q, d.R, d.S)
		if _, ok := za.Range.AreaMap[areaCube]; !ok {
			a := za.Range.registerArea(&areaCube)
			if a == nil {
				continue
			}
		}
		area := za.Range.AreaMap[areaCube]
		za.Neighbor[index] = area
		area.Link()

		// 存储边连接的区域
		for i := za.RingStartIndex; i < uint16(len(za.ChildrenOffset)); i++ {
			a := za.ChildrenOffset[i].EvenQToCube()
			si := i - za.RingStartIndex
			if DistanceWithCube(&a, &areaCube) == int(za.Range.Gap) {
				za.RingList[si].Neighbor = append(za.RingList[si].Neighbor, byte(index))
			}
		}
	}
}

func (za *ZeroCubeArea) Update() {
	// 更新block

}

//func (za *ZeroCubeArea) GetPoint(point *Vector2) uint16 {
//	//fmt.Println(za.IndexMap)
//	return za.IndexMap[*point]
//}

func (za *ZeroCubeArea) GetPath(container *ZeroCubeContainer, point *Cube, mask PathMask) ([]*ZeroCubePoint, bool) {
	pointIndex := za.CubeMap[*point]
	points := container.Points()
	visited, costed := container.Visited(len(za.ChildrenOffset))
	queue := container.List()
	queue = append(queue, pointIndex)
	blocks := container.Block()
	endArea := container.EndArea(za)
	endIndex := container.EndPoint.Index

	isEnd := pointIndex == endIndex
	end := pointIndex
	step := int(1)
	next := false
	visited[pointIndex] = -1
	for len(queue) > 0 {
		index := queue[0]
		queue = queue[1:]
		if index == end {
			next = true
		}
		cube := za.ChildrenOffset[uint16(index)].EvenQToCube()
		if endArea && index == endIndex && !isEnd {
			// 到达目标点
			p := container.Point()
			p.Area = za
			p.Cube = cube
			points = append(points, p)
			blocks = blocks[0:0]
			//fmt.Println("end", index, p.GetId(), za.Center)
			return points, true
		}
		if index >= za.RingStartIndex {
			//fmt.Println("add block", index)
			//blocks = append(blocks, index)
			t := len(blocks)
			for k, b := range blocks {
				if b == index {
					t = -1
					break
				}
				if b > index {
					t = k
					break
				}
			}
			//fmt.Println(t, index, blocks)

			if t >= 0 {
				if t == 0 {
					blocks = append(utils.UInt16Slice{index}, blocks...)
				} else if t == len(blocks) {
					blocks = append(blocks, index)
				} else {
					blocks = append(blocks[0:t], append(utils.UInt16Slice{index}, blocks[t:]...)...)
				}
			}
		}
		for d, _ := range CubeDirection {
			cube.Move(&CubeDirection[d])
			i, ok := za.CubeMap[cube]
			if !ok {
				continue
			}

			v := visited[i]
			if v < 0 {
				continue
			} else if v == 0 {
				if !za.Can(i, mask) {
					visited[i] = -1
					continue
				}
				costed[i] = za.Cost(i)
			}
			cost := costed[i] + int(step)
			if cost >= v && v > 0 {
				//fmt.Println("m", i, v, cost)
				continue
			}
			//fmt.Println("visited", i, v, cost)
			visited[i] = cost

			queue = append(queue, i)
			if next {
				end = i
			}
		}
		if next {
			step += 1
		}
		next = false
	}
	//fmt.Println("block", blocks)
	if len(blocks) == 0 {
		return points, false
	}
	// 排序关联点，找寻中间点，需要index是按一圈圈的顺序排列的
	//blocks.Sort()
	//blocks = container.Duplicate(blocks)
Select:
	ct := false
	minIndex := blocks[0]
	min := visited[minIndex]
	tmp := minIndex
	// 假设起点或者终点在边或者角上
	// 起点：不用得到起点所在的边，同时会向2个区域搜索，（角就向3个区域搜索），相连的边都不需要
	// 终点：同起点
	include := minIndex != pointIndex
	//old := blocks[0:1]
	if len(blocks) > 1 {
		blocks = blocks[1:]
		for i, index := range blocks {
			link := za.RingList[uint16(index)-za.RingStartIndex]
			if index == pointIndex {
				include = false
			}
			//fmt.Println(i, index, include, pointIndex)
			if tmp+1 != index || link.Corner() {
				if len(blocks) > i+1 {
					ct = true
					blocks = blocks[i+1:]
				}
				goto Point
			}
			cost := visited[index]
			if cost <= min {
				min = cost
				minIndex = index
			}
		}
	}
Point:
	if include {
		p := container.Point()
		p.Index = uint16(minIndex)
		//fmt.Println("ex", minIndex, min)
		p.NodeCost = min
		p.Area = za
		p.Cube = p.Area.ChildrenOffset[uint16(minIndex)].EvenQToCube()
		//p.Block = old
		points = append(points, p)
	}
	if ct {
		goto Select
	}
	return points, false
}

func (za *ZeroCubeArea) UpdatePoint(index uint16, identity int, t byte) {
	// 更改block
}

func (za *ZeroCubeArea) OtherArea(point *ZeroCubePoint) []*ZeroCubeArea {
	if point.Index < za.RingStartIndex {
		return []*ZeroCubeArea{za}
	} else {
		// 对应的另一个区域
		// 起点和终点如果是边，需要考虑所有连接
		r := za.RingList[point.Index-za.RingStartIndex]
		l := r.Neighbor
		ll := len(l)
		ll += 1
		al := make([]*ZeroCubeArea, ll)
		for i, index := range l {
			al[i] = za.Neighbor[index]
		}
		al[ll-1] = za
		return al
	}
}

func (za *ZeroCubeArea) IsInRing(index uint16) bool {
	return index >= za.RingStartIndex
}

func (za *ZeroCubeArea) Can(index uint16, mask PathMask) bool {
	return za.Grid.N(&za.ChildrenOffset[index], mask)
}
func (za *ZeroCubeArea) Cost(index uint16) int {
	return za.Grid.NodeCost(&za.ChildrenOffset[index])
}

type ZeroCubeLink struct {
	Neighbor []byte
	//Left     *ZeroLink
	//Right    *ZeroLink
}

func (link *ZeroCubeLink) Corner() bool {
	return len(link.Neighbor) > 1
}

type ZeroCubePoint struct {
	Index    uint16
	Cube     Cube
	Area     *ZeroCubeArea
	FromArea *ZeroCubeArea
	//IsHead   bool
	Step     byte
	NodeCost int
	//Block    []uint16

	Container *ZeroCubeContainer
}

func (current *ZeroCubePoint) Distance(end DijkstraNode) float64 {
	e := end.(*ZeroCubePoint)
	return float64(DistanceWithCube(&current.Cube, &e.Cube))
}

func (current *ZeroCubePoint) Can(mask PathMask) bool {
	//return current.Area.Can(current.Index, mask)
	// 可行走判断已完成
	return true
}

func (current *ZeroCubePoint) GetId() interface{} {
	return current.Cube
}

func (current *ZeroCubePoint) GetEdges(endLink map[interface{}]DijkstraEdge, mask PathMask) []DijkstraEdge {
	areas := current.Area.OtherArea(current)
	if areas == nil {
		return []DijkstraEdge{}
	}
	var points []*ZeroCubePoint
	//fmt.Println(current.Index, current.Area.ChildrenOffset[current.Index], current.Area.Point, len(areas))

	container := current.Container
	//id := current.Area.ChildrenOffset[current.Index]
	for _, area := range areas {
		//fmt.Println("area1", current.Area.Point, area.Point)
		if current.FromArea == area {
			continue
		}
		//pointIndex := area.CubeMap[current.Cube]
		nn, e := area.GetPath(container, &current.Cube, mask)
		points = append(points, nn...)
		//fmt.Println("area", current.Area.Point, area.Point, points, e)
		if e {
			break
		}
	}

	l := len(points)
	edges := make([]DijkstraEdge, l)
	for index, point := range points {
		point.FromArea = current.Area
		edge := current.Container.Edge()
		edge.Point = point
		edge.Cost = current.Cost(point)
		//fmt.Println("point", point.Index, &point.Area.ChildrenOffset[point.Index], point.Area.Point, edge.Cost)
		edges[index] = edge
	}
	return edges
}

func (current *ZeroCubePoint) Cost(point *ZeroCubePoint) float64 {
	return float64(point.NodeCost)
	//return float64(DistanceWithCube(&point.Cube, &current.Cube) + point.NodeCost)
}

func (current *ZeroCubePoint) Equal(other interface{}) bool {
	o := other.(*ZeroCubePoint)
	return current.Cube.Equals(&o.Cube)
}

type ZeroCubeEdge struct {
	Point *ZeroCubePoint
	Cost  float64
}

func (edge *ZeroCubeEdge) GetCost() float64 {
	//return float64(edge.Point.Step)
	return edge.Cost
}

func (edge *ZeroCubeEdge) Connect() DijkstraNode {
	return edge.Point
}
func (edge *ZeroCubeEdge) Switch(node DijkstraNode) {
	edge.Point = node.(*ZeroCubePoint)
}
